/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../helper_objs/head.h"
#include "../config/head.h"
#include "../macro/head.h"
#include "../impl/head.h"
#include "../msg/head.h"
#include "aio_udp.h"

namespace xos_net
{

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    aio_udp::aio_udp()
    {}

    aio_udp::~aio_udp()
    {}

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // net ret msg proc

    int aio_udp::proc( xos_box::i_msg * &pMsg )
    {
        int ret = 1;

        int nMsg = pMsg->get_msg();

        switch( nMsg )
        {
            // 
            // udp
            // 
            case xos_aio::i_aio::AIO_UDP_INIT_RET:
            {
                on_init( pMsg );
            }
            break;
            case xos_aio::i_aio::AIO_UDP_RECV_RET:
            {
                on_recv( pMsg );
            }
            break;
            case xos_aio::i_aio::AIO_UDP_SEND_RET:
            {
                on_send( pMsg );
            }
            break;
            case xos_aio::i_aio::AIO_UDP_SHUTDOWN_RET:
            {
                on_shutdown( pMsg );
            }
            break;
            case xos_aio::i_aio::AIO_UDP_CLOSE_RET:
            {
                on_close( pMsg );
            }
            break;
            default:
            {
                ret = 0;
            }
            break;
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // udp opt

    // 
    // m_bData[0] : true or false for result
    // 
    // m_nMsg[0]  : cmd type
    // 
    // m_nData[1] : local port
    // m_szStr[1] : local ip
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_init( xos_box::i_msg *& pMsg, connection * pConnect )
    {
        int ret = 0;

        if( !pMsg )
        {
            pMsg = mgr::container()->box()->msg();
        }
        udp * pUdp = pConnect->m_pUdp;

        bool bOK = pConnect && pConnect->is_udp() && pConnect->none();

        {
            pMsg->set_void( 0, pConnect->aio_connect() );
            pConnect->add_ref();
        }

        if( ( 0 == ret ) && !bOK )
        {
            pMsg->set_msg( 0, NET_UDP_INIT );
            pMsg->set_bool( 0, false );
            helper_net::get()->notify( pMsg );
            ret = 1;
        }

        if( 0 == ret )
        {
            pMsg->set_msg( 0, xos_aio::i_aio::AIO_UDP_INIT );
            pMsg->set_str( 1, pUdp->m_local_ip.c_str() );
            pMsg->set_int( 1, pUdp->m_nLocalPort );
        }

        if( 0 == ret )
        {
            pConnect->set_initing();
            ret = helper_net::aio()->post_request( pMsg );
        }

        xos_stl::release_interface( pMsg );

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // m_bData[1] : close after send
    // m_bData[2] : quit after send
    // 
    // m_nMsg[0]  : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[0] : send len
    // m_lpBuf[0] : send buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_send( xos_box::i_msg *& pMsg, connection * pConnect, xos_box::i_list * pList, const char * lpszIp, int nPort, bool bCloseAfterSend, bool bQuitAfterSend )
    {
        int ret = 0;

        xos_box::i_buf * pBuf = 0;

        while( ( pBuf = ( xos_box::i_buf * )pList->front( 0 ) ) )
        {
            pList->pop_front();
            if( !pList->front( 0 ) )
            {
                post_send( pMsg, pConnect, pBuf, lpszIp, nPort, bCloseAfterSend, bQuitAfterSend );
            }
            else
            {
                post_send( pMsg, pConnect, pBuf, lpszIp, nPort, false, false );
            }
        }

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // m_bData[1] : close after send
    // m_bData[2] : quit after send
    // 
    // m_nMsg[0]  : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[0] : send len
    // m_lpBuf[0] : send buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_send( xos_box::i_msg *& pMsg, connection * pConnect, xos_box::i_buf *& pBuf, const char * lpszIp, int nPort, bool bCloseAfterSend, bool bQuitAfterSend )
    {
        int ret = 0;

        if( !pMsg )
        {
            pMsg = mgr::container()->box()->msg();
        }
        udp * pUdp = pConnect->m_pUdp;

        bool bOK = pConnect && pConnect->is_udp() && pConnect->running() && pConnect->can_send();

        {
            LOG4( "aio_upd::post_send, port = %d", nPort );
            pConnect->add_ref();
        }

        {
            pMsg->set_bool( 1, bCloseAfterSend );
            pMsg->set_bool( 2, bQuitAfterSend );
            pMsg->set_str( 2, lpszIp );
            pMsg->set_int( 2, nPort );
            pMsg->set_buf( 0, pBuf );
            pMsg->set_void( 0, pConnect->aio_connect() );
        }

        if( ( 0 == ret ) && !bOK )
        {
            pMsg->set_msg( 0, NET_UDP_SEND );
            pMsg->set_bool( 0, false );
            helper_net::get()->notify( pMsg );
            pBuf = 0;
            ret = 1;
        }

        if( 0 == ret )
        {
            pMsg->set_msg( 0, xos_aio::i_aio::AIO_UDP_SEND );
            pMsg->set_void( 1, pUdp->m_pAioKey );
            pBuf = 0;
        }

        if( 0 == ret )
        {
            ret = helper_net::aio()->post_request( pMsg );
        }

        return ret;
    }

    // 
    // m_nMsg[0]  : cmd type
    // 
    // m_nData[1] : how
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_shutdown( xos_box::i_msg *& pMsg, connection * pConnect, int how )
    {
        int ret = 0;

        udp * pUdp = pConnect->m_pUdp;

        bool bOK = pConnect && pConnect->is_udp() && pConnect->running();

        if( ( 0 == ret ) && !bOK )
        {
            ret = 1;
        }

        if( 0 == ret )
        {
            pConnect->m_nReadWritePermmit &= ~how;
        }

        if( ( 0 == ret ) && !pMsg )
        {
            pMsg = mgr::container()->box()->msg();
        }

        if( 0 == ret )
        {
            pMsg->set_msg( 0, xos_aio::i_aio::AIO_UDP_SHUTDOWN );
            pMsg->set_void( 0, pConnect->aio_connect() );
            pMsg->set_void( 1, pUdp->m_pAioKey );
            pMsg->set_int( 1, how );
        }

        if( 0 == ret )
        {
            pConnect->add_ref();
            ret = helper_net::aio()->post_request( pMsg );
        }

        LOG4( "udp post shutdown: ( %s, %d )", pUdp->m_local_ip.c_str(), pUdp->m_nLocalPort );

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nMsg[0]  : cmd type
    // 
    // m_nData[1] : linger.l_onoff
    // m_nData[2] : linger.l_linger
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_close( xos_box::i_msg *& pMsg, connection * pConnect, int onoff, int linger, bool bNotifyIfAlreadyClosed )
    {
        int ret = 0;

        bool bOK = ( !pConnect->closing() && !pConnect->closed() && !pConnect->none() && !pConnect->init_failed() );
        udp * pUdp = pConnect->m_pUdp;

        if( !pMsg )
        {
            pMsg = mgr::container()->box()->msg();
        }

        if( 0 == ret )
        {
            pMsg->set_bool( 0, false );
            pMsg->set_msg( 0, xos_aio::i_aio::AIO_UDP_CLOSE );
            pMsg->set_int( 1, onoff );
            pMsg->set_int( 2, linger );
            pMsg->set_void( 0, pConnect->aio_connect() );
            pMsg->set_void( 1, pUdp->m_pAioKey );
        }

        if( ( 0 == ret ) && !bOK )
        {
            if( bNotifyIfAlreadyClosed )
            {
                helper_net::aio()->post_request( pMsg );
            }
            else
            {
                // 没有成功执行close动作，要减计数
                // 因为发起方已经加了计数，而close失败的情况下是不向发起方返回关闭结果的
                // 所以需要在这里平衡一下计数
                pConnect->release_ref();
            }
            ret = 1;
        }

        if( 0 == ret )
        {
            pConnect->add_ref();
            pConnect->set_closing();
            helper_net::aio()->post_request( pMsg );
        }

        if( 0 == ret )
        {
            LOG4( "udp post close: (%x)( %s, %d ) = %d", pConnect, pUdp->m_local_ip.c_str(), pUdp->m_nLocalPort, pConnect->closing() );
        }

        xos_stl::release_interface( pMsg );

        return ret;
    }

    int aio_udp::post_close( xos_box::i_msg *& pMsg, connection * pConnect, bool bNotifyIfAlreadyClosed )
    {
        int ret = 0;
        ret = post_close( pMsg, pConnect, 1, 1, bNotifyIfAlreadyClosed );
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // udp

    // 
    // m_bData[0] : true or false for result
    // 
    // m_nMsg[0]  : cmd type
    // 
    // m_nData[1] : local port
    // m_szStr[1] : local ip
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::on_init( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        connection * pConnect = ( connection * )(xos_aio::i_connect*)pMsg->get_void( 0, 0 );
        udp * pUdp = pConnect->m_pUdp;

        void * pAioKey = pMsg->get_void( 1, 0 );
        bool bRet = pMsg->get_bool( 0, 0 );

        if( bRet )
        {
            pUdp->m_local_ip = pMsg->get_str( 1, 0, 0, 0 );
            pUdp->m_nLocalPort = pMsg->get_int( 1, 0 );
            pUdp->m_pAioKey = pAioKey;
        }

        if( bRet )
        {
            LOG4( "udp init success = (%s:%d)", pUdp->m_local_ip.c_str(), pUdp->m_nLocalPort );
            pConnect->set_running();
        }
        else
        {
            LOG4( "udp init failed = (%s:%d)", pUdp->m_local_ip.c_str(), pUdp->m_nLocalPort );
            pConnect->set_init_failed();
        }

        {
            pMsg->set_msg( 0, NET_UDP_INIT );
            pMsg->set_void( 0, pConnect );
            helper_net::get()->notify( pMsg );
        }

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nMsg[0]  : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[0] : recv len
    // m_lpBuf[0] : recv buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::on_recv( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        connection * pConnect = (connection *)(xos_aio::i_connect *)pMsg->get_void( 0, 0 );

        xos_box::i_buf * pBuf = pMsg->get_buf( 0, 0 );
        bool bRet = pMsg->get_bool( 0, 0 );

        if( bRet )
        {
            pMsg->set_msg( 0, NET_UDP_RECV );
            pMsg->set_void( 0, pConnect );
            pConnect->add_ref();
            helper_net::get()->notify( pMsg );
            pBuf = 0;
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nMsg[0]  : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[0] : send len
    // m_lpBuf[0] : send buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::on_send( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        connection * pConnect = (connection *)(xos_aio::i_connect *)pMsg->get_void( 0, 0 );
        //udp * pUdp = pConnect->m_pUdp;

        {
            pMsg->set_msg( 0, NET_UDP_SEND );
            pMsg->set_void( 0, pConnect );
            helper_net::get()->notify( pMsg );
        }

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nMsg[0]  : cmd type
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::on_shutdown( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        connection * pConnect = (connection *)(xos_aio::i_connect *)pMsg->get_void( 0, 0 );
        udp * pUdp = pConnect->m_pUdp;

        LOG4( "udp shutdown = (%s:%d)", pUdp->m_local_ip.c_str(), pUdp->m_nLocalPort );

        {
            pMsg->set_msg( 0, NET_UDP_SHUTDOWN );
            pMsg->set_void( 0, pConnect );
            helper_net::get()->notify( pMsg );
        }

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nMsg[0]  : cmd type
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::on_close( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        connection * pConnect = (connection *)(xos_aio::i_connect *)pMsg->get_void( 0, 0 );
        udp * pUdp = pConnect->m_pUdp;

        LOG4( "udp closed = (%s:%d)", pUdp->m_local_ip.c_str(), pUdp->m_nLocalPort );

        {
            pMsg->set_msg( 0, NET_UDP_CLOSE );
            pMsg->set_void( 0, pConnect );
            pConnect->set_closed();
            helper_net::get()->notify( pMsg );
        }

        return ret;
    }

} // xos_net
